package org.zoomdev.zoom.asm;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import java.util.HashMap;
import java.util.Map;

public class AnnotationBuilder {
    private final String descriptor;
    private final Map<String, Object> values = new HashMap<>();

    public AnnotationBuilder(String descriptor) {
        this.descriptor = descriptor;
    }

    public AnnotationBuilder withValue(String name, Object value) {
        values.put(name, value);
        return this;
    }

    public void apply(ClassWriter cw) {
        cw.visitAnnotation(descriptor, true);
    }

    public void apply(FieldVisitor fv) {
        org.objectweb.asm.AnnotationVisitor av = fv.visitAnnotation(descriptor, true);
        if (av != null) {
            for (Map.Entry<String, Object> entry : values.entrySet()) {
                visitValue(av, entry.getKey(), entry.getValue());
            }
            av.visitEnd();
        }
    }

    public void apply(MethodVisitor mv) {
        org.objectweb.asm.AnnotationVisitor av = mv.visitAnnotation(descriptor, true);
        if (av != null) {
            for (Map.Entry<String, Object> entry : values.entrySet()) {
                visitValue(av, entry.getKey(), entry.getValue());
            }
            av.visitEnd();
        }
    }

    private void visitValue(org.objectweb.asm.AnnotationVisitor av, String name, Object value) {
        if (value instanceof String) {
            av.visit(name, (String) value);
        } else if (value instanceof Integer) {
            av.visit(name, (Integer) value);
        } else if (value instanceof Boolean) {
            av.visit(name, (Boolean) value);
        } else if (value instanceof Float) {
            av.visit(name, (Float) value);
        } else if (value instanceof Double) {
            av.visit(name, (Double) value);
        } else if (value instanceof Enum<?>) {
            Enum<?> enumValue = (Enum<?>) value;
            av.visitEnum(name, Type.getDescriptor(enumValue.getClass()), enumValue.name());
        } else if (value instanceof Class<?>) {
            av.visit(name, Type.getType((Class<?>) value));
        }
    }
}